home *** CD-ROM | disk | FTP | other *** search
/ NetNews Offline 2 / NetNews Offline Volume 2.iso / news / comp / lang / c++-part2 / 11377 < prev    next >
Encoding:
Internet Message Format  |  1996-08-05  |  4.4 KB

  1. Path: ix.netcom.com!netnews
  2. From: jlilley@ix.netcom.com (John Lilley)
  3. Newsgroups: comp.lang.c++
  4. Subject: Re: Virtual Base Class
  5. Date: 14 Mar 1996 05:54:17 GMT
  6. Organization: Netcom
  7. Message-ID: <4i8ca9$57m@ixnews3.ix.netcom.com>
  8. References: <313F98D0.102E@ucla.edu> <4i1k92$3n8@apoll.informatik.uni-bonn.de> <4i49os$20j@apoll.informatik.uni-bonn.de>
  9. NNTP-Posting-Host: den-co20-12.ix.netcom.com
  10. Mime-Version: 1.0
  11. Content-Type: Text/Plain; charset=US-ASCII
  12. X-NETCOM-Date: Wed Mar 13  9:54:17 PM PST 1996
  13. X-Newsreader: WinVN 0.99.7
  14.  
  15. In article <4i49os$20j@apoll.informatik.uni-bonn.de>, schregle@Mars says...
  16. >Yep, I'm replying to myself... 
  17. >Roland Schregle (schregle@zeus.informatik.uni-bonn.de) wrote:
  18. >: Dennis Rahaman (dennisr@ucla.edu) wrote:
  19. >: : This is what I want to do:
  20. >
  21. >: :               a
  22. >: :             /   \
  23. >: :            b     c
  24. >: :             \   /
  25. >: :               d
  26.  
  27. [... class derivation details omitted ... ]
  28.  
  29. >: : void f ()
  30. >: :   {
  31. >: :   a a1;
  32. >: :   d* pd = (d*) &a1;  // error: can't cast virtual base to derived
  33. >: :   }
  34. >
  35. >: : ///////////////////////////////////////////////////////////
  36. >: : Can someone explain why I can't cast a virtual base class to a derived 
  37. >: : class?
  38.  
  39.  
  40. The previous posters, while correctly deploring the concept of 
  41. "downcasting" from a base class pointer to a derived class, failed 
  42. to state clearly why a *virtual* base class is so much different and
  43. intractible than a non-virtual base class.  After all, dynamic casting
  44. has been added to C++ to handle the non-virtual case, why couldn't
  45. it do the virtual case as well?
  46.  
  47. Subsequent posters' attempts to perform the cast may work but are 
  48. wrong, such as the one the gets the compiler to shut up 
  49. buts yields the wrong value:
  50.  
  51. >:  d* pd = (d*)(void*)&a1;
  52.  
  53. Or the one that gets the compiler to shut up and yields the
  54. right value by the trick of finding the differences between
  55. the two pointers when you cast a derived to a virtual base.
  56. But even this won't work all of the time (I'll explain later):
  57.  
  58. >   a a1;
  59. >   d* testy = new d;
  60. >   int offset = (void*)testy - (void*)(a*)testy;
  61. >   delete testy;
  62. >   d* pd = (d*)((void*)a1 + offset);
  63.  
  64.  
  65. The key concept is that virtual base classes are just like virtual
  66. methods in that the physical address is not known until an actual object
  67. is encountered at runtime.  Runtime.  Get it?  The reason is that
  68. since there can only be one virtual base class contained in a derived
  69. object, regardless of how many times it is encountered in the derivation
  70. tree, the compiler must be able to change the offset between the
  71. virtual base class and any other class in the tree.  In other
  72. words, the offset between "testy" and "(a*)testy" may change
  73. if testy points to a subclass of d instead of an actual d.  Since it
  74. is perfectly and always valid to use pointers to derived classes anywhere
  75. you have a pointer to a base, then the above offset computation may fail
  76. if you derive from d and start using pointers to that subclass intermixed 
  77. with pointers to d, which is, after all, what OOP is about.
  78.  
  79. The above examples do not really state the problem correctly, because they
  80. start with real a's and d's as opposed to pointers.  Let's look
  81. at a more realistic example (still assuming the above class hierarchy),
  82. and assume that the above computed offset was computed somewhere and
  83. squirreled away:
  84.  
  85. void func1(d* d1)
  86. {
  87.    a* a1 = d1;
  88.    ...
  89.    // somehow "know" that a1 is really a d*
  90.    d* d2 = (d*)((void*)a1 + offset);
  91.    d2->d_method();
  92. }
  93.  
  94. Now consider:
  95.  
  96. class e : public a
  97. {
  98.    ... 
  99. };
  100.  
  101. class f : public d, public e
  102. {
  103.    ...
  104. };
  105.  
  106. Which gives a derivation tree like:
  107.  
  108.            a
  109.          / | \
  110.         /  |  \
  111.        b   c   e
  112.         \ /   /
  113.          d   /
  114.           \ /
  115.            f
  116.  
  117. void func2()
  118. {
  119.    d d1;
  120.    f f1;
  121.    func1(&d1);    // syntactically valid and probably works
  122.    func1(&f1);    // syntactically valid and almost certainly fails
  123. }
  124.  
  125. In the above case, even though both d1 and f1 are "really" d's,
  126. the offset between the 'd' part and the 'a' part is not necessarily
  127. the same because of the compiler's freedom to place the single 'a'
  128. anywhere relative to the other class parts.
  129.  
  130. The worst result of the above attempts is that they might work until
  131. someone uses a derived class, and then they will fail in a mysterious
  132. way, which even when detected will leave the poor maintenance programmer
  133. with nowhere to go but redesigning the code from scratch.
  134.  
  135. Moral: Stop attempting to defeat the compiler and try a simple approach.
  136.  
  137. John Lilley
  138. Nerds for Hire, Inc.
  139.  
  140.